

package uplink

import (
	"context"
	"errors"
	"fmt"
	"github.com/zeebo/errs"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
	//"storj.io/common/identity"
	"storj.io/common/pb"
	//"storj.io/common/peertls/tlsopts"
	"storj.io/common/rpc"
	"storj.io/common/rpc/quic"
	"storj.io/uplink/contact"
	"storj.io/uplink/lifecycle"
	"storj.io/uplink/server"
)

type HolePunching struct {
	Log      *zap.Logger

	Servers  *lifecycle.Group

	Dialer  rpc.Dialer
	simpleServer  *server.SimpleServer

	Contact struct {
		//Service  *contact.Service
		Endpoint *contact.Endpoint
	}
}


func NewServer(ctx  context.Context)(*HolePunching,error) {

	log := zap.L()

	fmt.Println("########  NewServer ###### ")

	peer := &HolePunching{
		Log:      log,
		Servers:  lifecycle.NewGroup(log.Named("servers")),
		//Services: lifecycle.NewGroup(log.Named("services")),
	}

	var sc  = server.Config{}


	//ident, err := identity.NewFullIdentity(ctx, identity.NewCAOptions{
	//	Difficulty:  0,
	//	Concurrency: 1,
	//})
	//if err != nil {
	//	return nil, packageError.Wrap(err)
	//}

	//tlsOptions, err := tlsopts.NewOptions(ident, sc.Config, nil)

	//todo 使用tlsopts.NewOptions与卫星通讯失败
	//todo tlsopts.NewOptions  与getProcessTLSOptions 有什么区别？

	tlsOpt, err := getProcessTLSOptions(ctx)
	peer.Dialer = rpc.NewDefaultDialer(tlsOpt)

	sc.Address = "0.0.0.0:11008"
	peer.simpleServer, err = server.NewSimpleServer(log.Named("server"), tlsOpt, sc)
	if err != nil {
		return nil, errors.New("")
	}

	peer.Dialer.Pool = rpc.NewDefaultConnectionPool()
	//peer.Dialer.DialTimeout = time.Second* 1
	peer.Dialer.Connector = quic.NewDefaultConnectorWithOpt(nil,true, peer.simpleServer.UdpConn())

	peer.Servers.Add(lifecycle.Item{
		Name: "server",
		Run: func(ctx context.Context) error {
			// Don't change the format of this comment, it is used to figure out the node id.
			return peer.simpleServer.Run(ctx)
		},
		Close: peer.simpleServer.Close,
	})
	{	//contact
		peer.Contact.Endpoint = contact.NewEndpoint(peer.Log.Named("contact:endpoint"))
		if err := pb.DRPCRegisterNode(peer.simpleServer.DRPC(), peer.Contact.Endpoint); err != nil {
			return nil, errs.Combine(err, peer.Close())
		}
	}

	return peer,nil

}
func (peer *HolePunching)GetEndpoint()*contact.Endpoint{
	return peer.Contact.Endpoint
}
func (peer *HolePunching)GetRemoteAdress(node string)string{
	if peer.Contact.Endpoint == nil{
		return ""
	}
	return  peer.Contact.Endpoint.GetRemoteAdress(node)
}
func (peer *HolePunching)GetDailer() rpc.Dialer{

	return peer.Dialer
}

// Run runs satellite garbage collection until it's either closed or it errors.
func (peer *HolePunching) Run(ctx context.Context) (err error) {
	defer mon.Task()(&ctx)(&err)

	group, ctx := errgroup.WithContext(ctx)

	peer.Servers.Run(ctx, group)

	return group.Wait()
}

// Close closes all the resources.
func (peer *HolePunching) Close() error {
	return errs.Combine(
		peer.Servers.Close(),
	)
}